/**
 * class: MsgInjector
 * 		- A preemptive Message Injector.
 * 		- It enables the user to inject arbitrary messages into the network.    
 */

package distributed;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Collection;
import java.util.Iterator;
import java.util.StringTokenizer;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

import common.Config;
import common.Def;
import common.Message;
import common.NodeRecord;
import common.Utils;

public class MsgInjector extends JFrame implements Runnable, ActionListener, KeyListener{
	
	private JPanel content;
	private JTextField input;
	private JButton send_button;
	
	private Middleware network;
	
	public MsgInjector( Middleware new_network ){
		network = new_network;
		
		content = new JPanel();
		
		input = new JTextField( 30 );
		input.addKeyListener( this );
		content.add( input );
		
		send_button = new JButton( "Send" );
		send_button.addActionListener( this );
		content.add( send_button );
		
		
		setContentPane( content );
		setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE );
        setTitle( "Message Injector" );
        pack();
        
        setVisible( true );
	}
	
	public void run(){
		
		//int send_status;
		
		//str = JOptionPane.showInputDialog( this, "Inject a message." );
		//send_status = send( str );
		
		//switch( send_status ){
		//}
	}

	public synchronized int send( String str ){
		Node receiver = null;
		Message msg = msg_gen( str );	// msg gen
		
		if( msg != null ){
			if( msg.getRip() == 0 ){
				long temp = 0L;
				Collection<Node> all_nodes = network.routing_table.values();
				Iterator<Node> itr = all_nodes.iterator();
				while( itr.hasNext() ){
					Node node = itr.next();
					if( node.get_pid() == msg.getRpid() & temp == 0 ){
						temp = node.get_ip(); 
					}else if( node.get_pid() == msg.getRpid() & temp != 0 ){
						// identical PID's (hardly happen)
						pop_log( "Identical PID's. Please specify the IP address." );
						return 0;						
					}
				}
				msg.setRip( temp );
			}
			
			try{
				receiver = network.routing_table.get( new NodeRecord( msg.getRpid(), msg.getRip() ) );
			}catch( NullPointerException e ){}
			
			if( receiver == null ){
				pop_log( "Target is not found." );
				return 0;
			}
				
			receiver.msg_q.offer( msg );

			// empty text filed
			input.setText( "" );
			
			print_log( "Msg '" + str + "' is sent to " + msg.getRpid() + "." );
			return 1;
		}
		
		return 0;
	}
	
	public synchronized Message msg_gen( String str ){
		int msg_header;
		int msg_r_pid;
		long msg_r_ip = 0L;
		
		// parsing
		StringTokenizer st = new StringTokenizer( str );
		
		// param 1
		if( !st.hasMoreTokens() )
			return null;
		String token = st.nextToken();
		
		if( token.toLowerCase().equals( "kill" ) )
			msg_header = Def.ROOT_KILL;
		else if( token.toLowerCase().equals( "sleep" ) )
			msg_header = Def.ROOT_SLEEP;
		else{
			//print_log( "unknown command", Def.STDERR );
			pop_log( "Unknown command." );
			
			return null;
		}

		
		// param 2 - pid
		if( !st.hasMoreTokens() ){
			pop_log( "Syntax error: the correct grammar is (kill|sleep) (PID) [IP]." );
			return null;
		}else{
			// blah
			token = st.nextToken();
			if( Utils.isNumber( token ) ){
				msg_r_pid = Integer.parseInt( token );
			}else{
				pop_log( "Syntax error: PID should be a number." );
				return null;
			}
		}
		
		// param 3 - ip
		if( st.hasMoreTokens() ){
			token = st.nextToken();
			
			msg_r_ip = Utils.stringToIp( token );
			
			if( msg_r_ip == -1 ){
				pop_log( "Syntax error: illegal IP address." );
				return null;
			}
		}
		
		return new Message( -1, msg_header, msg_r_pid, msg_r_ip, -1, Def.MIDDLEWARE_IP );
	}

	public void pop_log( String log ){
		JOptionPane.showMessageDialog( this, log, "Result", JOptionPane.ERROR_MESSAGE );
	}
	
	public void print_log( String log ){
		print_log( log, Def.STDOUT );
	}
	
	public void print_log( String log, byte target ){
		if( !Config.VERBOSE )
			return;
		
		if( target == Def.STDOUT ){
			System.out.print( "*MSG|injector* " );
			System.out.println( log );
		}else if( target == Def.STDERR ){
			System.err.print( "*ERR|injector* " );
			System.err.println( log );
		}
	}
	
	public void common_action(){
		String str = input.getText();
		
		// send
		send( str );
		
		// log
		//print_log( str );
	}
	
	public void actionPerformed( ActionEvent ae ){
		if( ae.getActionCommand().equals( "Send" ) && !input.getText().equals( "" ) ){
			common_action();
		}
	}

	public void keyPressed( KeyEvent ke ){
		if( ke.getKeyChar() == '\n' && !input.getText().equals( "" ) ){
			common_action();
		}
	}

	public void keyReleased( KeyEvent ke ){}
	public void keyTyped( KeyEvent ke ){}
}
